home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / Book Chapters / 03 - Advanced Graphics / Example 7 / demo.c < prev    next >
Text File  |  1995-03-06  |  17KB  |  560 lines

  1. //
  2. //    File: demo.c
  3. //
  4. //    This file contains the routines that control the demo.
  5. //
  6. //    2/19/95 -- Created by Mick
  7. //
  8.  
  9. // include files
  10.  
  11. #include "global.h"
  12.  
  13. #include <Palettes.h>
  14. #include <QDOffscreen.h>
  15.  
  16. #include "demo.h"
  17.  
  18. #include "background.h"
  19. #include "main.h"
  20. #include "sprite.h"
  21. #include "update.h"
  22.  
  23. // defines for this file
  24.  
  25. #define kMaxObjects            10                // the maximum number of objects in the demo
  26. #define kClipRectInset        100            // the amount to inset the clip rect
  27.  
  28. // typedefs for this file
  29.  
  30. typedef struct
  31. {
  32.     tSpriteInfo *fSpriteInfo;                                            // the information on the sprite for this object
  33.     unsigned char fIsVisible;                                    // should we draw this object
  34.     unsigned char fWasVisible;                            // was this object visible last time?
  35.     signed long fXPos;                                                        // the x position of this object ( 16:16 fixed )
  36.     signed long fYPos;                                                        // the y position of this object ( 16:16 fixed )
  37.     signed long fDeltaX;                                                    // the x coord of the velocity of this object ( 16:16 fixed )
  38.     signed long fDeltaY;                                                    // the y coord of the velocity of this object ( 16:16 fixed )
  39.     signed short fIntXPos;                                            // the x position as an integer
  40.     signed short fIntYPos;                                            // the y position as an integer
  41. } tObjectInfo;
  42.  
  43. // global function declarations
  44.  
  45. void startupDemo( void );
  46. void shutdownDemo( void );
  47. void demoKey( unsigned char inKey );
  48. void doDemoFrame( void );
  49.  
  50. // global data owned by this file
  51.  
  52. GWorldPtr gOffscreenBuffer;                                        // the port and gdevice of the offscreen buffer
  53. PixMapHandle gOffscreenPixels;                                // the actual pixmap of the offscreen buffer
  54. Rect gOffscreenRect;                                                                // the size of the offscreen buffer
  55.  
  56. // local function declarations
  57.  
  58. static void moveObjects( void );                                                    // move all the objects
  59. static void blitToScreen( Rect *inCopyRect );            // copy data from offscreen to onscreen
  60.  
  61. // static data
  62.  
  63. static tObjectInfo sObjects[ kMaxObjects ];                    // the data for all the objects
  64. static unsigned char sShowUpdateAreaFlag;            // should we show the update area?
  65. static unsigned char sStepMode;                                            // are we running step by step
  66.  
  67. // functions
  68.  
  69. //
  70. //    startupDemo -
  71. //
  72. //    Create and load all the buffers and data needed for the demo.
  73. //
  74.  
  75. void startupDemo( void )
  76. {
  77.     unsigned short indexCounter;                // a counter to scan the object array
  78.  
  79.     // create the offscreen gworld
  80.     NewGWorld( &gOffscreenBuffer, 8, &( gMainWindow->portRect ), gAppColorTable, ( GDHandle )kNil, keepLocal );
  81.  
  82.     // get the pixel map and rect
  83.     gOffscreenPixels = GetGWorldPixMap( gOffscreenBuffer );
  84.     gOffscreenRect = gMainWindow->portRect;
  85.  
  86.     // load the sprite
  87.     for( indexCounter = 0; indexCounter < kMaxObjects; indexCounter++ )
  88.         {
  89.             // load the sprites
  90.             sObjects[ indexCounter ].fSpriteInfo = loadSprite( kNumberOne + indexCounter );
  91.             
  92.             // give it a random location and velocity
  93.             sObjects[ indexCounter ].fIntXPos = Random() & 0x00FF;
  94.             sObjects[ indexCounter ].fIntYPos = Random() & 0x00FF;
  95.             sObjects[ indexCounter ].fXPos = ( sObjects[ indexCounter ].fIntXPos ) << 16;
  96.             sObjects[ indexCounter ].fYPos = ( sObjects[ indexCounter ].fIntYPos ) << 16;
  97.             sObjects[ indexCounter ].fDeltaX = Random() << 4;
  98.             sObjects[ indexCounter ].fDeltaY = Random() << 4;
  99.             
  100.             // initially, it is visible (but it was not last frame)
  101.             sObjects[ indexCounter ].fIsVisible = kTrue;
  102.             sObjects[ indexCounter ].fWasVisible = kFalse;
  103.         }
  104.     
  105.     // startup the background
  106.     startupBackground();
  107.     
  108.     // make sure that the background gets fully drawn and copied
  109.     restoreBackground( &gOffscreenRect );
  110.     blitToScreen( &gOffscreenRect );
  111.     
  112.     // set the initial flags
  113.     sStepMode = kFalse;
  114.     sShowUpdateAreaFlag = kFalse;
  115. }
  116.  
  117.  
  118. //
  119. //    shutdownDemo -
  120. //
  121. //    Release all the memory used by the demo.
  122. //
  123.  
  124. void shutdownDemo( void )
  125. {
  126.     unsigned short indexCounter;                // a counter to scan the object array
  127.     
  128.     // dump the sprites
  129.     for( indexCounter = 0; indexCounter < kMaxObjects; indexCounter++ )
  130.         {
  131.             disposeSprite( sObjects[ indexCounter ].fSpriteInfo );
  132.         }
  133.     
  134.     // shutdown the background
  135.     shutdownBackground();
  136.     
  137.     // dump the offscreen gworld
  138.     DisposeGWorld( gOffscreenBuffer );
  139. }
  140.  
  141.  
  142. //
  143. //    demoKey -
  144. //
  145. //    Handle a key stroke -- allows the user to set options while the demo runs.
  146. //
  147.  
  148. void demoKey( unsigned char inKey )
  149. {
  150.     // depending on the key pressed
  151.     switch( inKey )
  152.         {
  153.             case 'u':        // turn show update are on/off
  154.                 sShowUpdateAreaFlag = !sShowUpdateAreaFlag;
  155.                 break;
  156.             case 's':        // turn step mode on and off
  157.                 sStepMode = !sStepMode;
  158.                 break;
  159.             case ' ':        // step one frame (if we are in step mode)
  160.                 if ( sStepMode )
  161.                     {
  162.                         // allow one frame, by temporarily turning off step mode
  163.                         sStepMode = kFalse;
  164.                         doDemoFrame();
  165.                         sStepMode = kTrue;
  166.                     }
  167.                 break;
  168.             case '+':        // add more rects to the update
  169.                 increaseUpdateRects();
  170.                 break;
  171.             case '-':        // reduce the number of rects to update with
  172.                 decreaseUpdateRects();
  173.                 break;
  174.                                 
  175.             case '1':        // turn this number on and off
  176.                 sObjects[ 0 ].fIsVisible = !sObjects[ 0 ].fIsVisible;
  177.                 break;
  178.             case '2':        // turn this number on and off
  179.                 sObjects[ 1 ].fIsVisible = !sObjects[ 1 ].fIsVisible;
  180.                 break;
  181.             case '3':        // turn this number on and off
  182.                 sObjects[ 2 ].fIsVisible = !sObjects[ 2 ].fIsVisible;
  183.                 break;
  184.             case '4':        // turn this number on and off
  185.                 sObjects[ 3 ].fIsVisible = !sObjects[ 3 ].fIsVisible;
  186.                 break;
  187.             case '5':        // turn this number on and off
  188.                 sObjects[ 4 ].fIsVisible = !sObjects[ 4 ].fIsVisible;
  189.                 break;
  190.             case '6':        // turn this number on and off
  191.                 sObjects[ 5 ].fIsVisible = !sObjects[ 5 ].fIsVisible;
  192.                 break;
  193.             case '7':        // turn this number on and off
  194.                 sObjects[ 6 ].fIsVisible = !sObjects[ 6 ].fIsVisible;
  195.                 break;
  196.             case '8':        // turn this number on and off
  197.                 sObjects[ 7 ].fIsVisible = !sObjects[ 7 ].fIsVisible;
  198.                 break;
  199.             case '9':        // turn this number on and off
  200.                 sObjects[ 8 ].fIsVisible = !sObjects[ 8 ].fIsVisible;
  201.                 break;
  202.             case '0':        // turn this number on and off
  203.                 sObjects[ 9 ].fIsVisible = !sObjects[ 9 ].fIsVisible;
  204.                 break;
  205.                 
  206.             case '!':        // freeze/unfreeze this number
  207.                 if ( sObjects[ 0 ].fDeltaX == 0 && sObjects[ 0 ].fDeltaY == 0 )
  208.                     {
  209.                         sObjects[ 0 ].fDeltaX = Random() << 4;
  210.                         sObjects[ 0 ].fDeltaY = Random() << 4;
  211.                     }
  212.                 else
  213.                     {
  214.                         sObjects[ 0 ].fDeltaX = 0;
  215.                         sObjects[ 0 ].fDeltaY = 0;
  216.                     }
  217.                 break;
  218.             case '@':        // freeze/unfreeze this number
  219.                 if ( sObjects[ 1 ].fDeltaX == 0 && sObjects[ 1 ].fDeltaY == 0 )
  220.                     {
  221.                         sObjects[ 1 ].fDeltaX = Random() << 4;
  222.                         sObjects[ 1 ].fDeltaY = Random() << 4;
  223.                     }
  224.                 else
  225.                     {
  226.                         sObjects[ 1 ].fDeltaX = 0;
  227.                         sObjects[ 1 ].fDeltaY = 0;
  228.                     }
  229.                 break;
  230.             case '#':        // freeze/unfreeze this number
  231.                 if ( sObjects[ 2 ].fDeltaX == 0 && sObjects[ 2 ].fDeltaY == 0 )
  232.                     {
  233.                         sObjects[ 2 ].fDeltaX = Random() << 4;
  234.                         sObjects[ 2 ].fDeltaY = Random() << 4;
  235.                     }
  236.                 else
  237.                     {
  238.                         sObjects[ 2 ].fDeltaX = 0;
  239.                         sObjects[ 2 ].fDeltaY = 0;
  240.                     }
  241.                 break;
  242.             case '$':        // freeze/unfreeze this number
  243.                 if ( sObjects[ 3 ].fDeltaX == 0 && sObjects[ 3 ].fDeltaY == 0 )
  244.                     {
  245.                         sObjects[ 3 ].fDeltaX = Random() << 4;
  246.                         sObjects[ 3 ].fDeltaY = Random() << 4;
  247.                     }
  248.                 else
  249.                     {
  250.                         sObjects[ 3 ].fDeltaX = 0;
  251.                         sObjects[ 3 ].fDeltaY = 0;
  252.                     }
  253.                 break;
  254.             case '%':        // freeze/unfreeze this number
  255.                 if ( sObjects[ 4 ].fDeltaX == 0 && sObjects[ 4 ].fDeltaY == 0 )
  256.                     {
  257.                         sObjects[ 4 ].fDeltaX = Random() << 4;
  258.                         sObjects[ 4 ].fDeltaY = Random() << 4;
  259.                     }
  260.                 else
  261.                     {
  262.                         sObjects[ 4 ].fDeltaX = 0;
  263.                         sObjects[ 4 ].fDeltaY = 0;
  264.                     }
  265.                 break;
  266.             case '^':        // freeze/unfreeze this number
  267.                 if ( sObjects[ 5 ].fDeltaX == 0 && sObjects[ 5 ].fDeltaY == 0 )
  268.                     {
  269.                         sObjects[ 5 ].fDeltaX = Random() << 4;
  270.                         sObjects[ 5 ].fDeltaY = Random() << 4;
  271.                     }
  272.                 else
  273.                     {
  274.                         sObjects[ 5 ].fDeltaX = 0;
  275.                         sObjects[ 5 ].fDeltaY = 0;
  276.                     }
  277.                 break;
  278.             case '&':        // freeze/unfreeze this number
  279.                 if ( sObjects[ 6 ].fDeltaX == 0 && sObjects[ 6 ].fDeltaY == 0 )
  280.                     {
  281.                         sObjects[ 6 ].fDeltaX = Random() << 4;
  282.                         sObjects[ 6 ].fDeltaY = Random() << 4;
  283.                     }
  284.                 else
  285.                     {
  286.                         sObjects[ 6 ].fDeltaX = 0;
  287.                         sObjects[ 6 ].fDeltaY = 0;
  288.                     }
  289.                 break;
  290.             case '*':        // freeze/unfreeze this number
  291.                 if ( sObjects[ 7 ].fDeltaX == 0 && sObjects[ 7 ].fDeltaY == 0 )
  292.                     {
  293.                         sObjects[ 7 ].fDeltaX = Random() << 4;
  294.                         sObjects[ 7 ].fDeltaY = Random() << 4;
  295.                     }
  296.                 else
  297.                     {
  298.                         sObjects[ 7 ].fDeltaX = 0;
  299.                         sObjects[ 7 ].fDeltaY = 0;
  300.                     }
  301.                 break;
  302.             case '(':        // freeze/unfreeze this number
  303.                 if ( sObjects[ 8 ].fDeltaX == 0 && sObjects[ 8 ].fDeltaY == 0 )
  304.                     {
  305.                         sObjects[ 8 ].fDeltaX = Random() << 4;
  306.                         sObjects[ 8 ].fDeltaY = Random() << 4;
  307.                     }
  308.                 else
  309.                     {
  310.                         sObjects[ 8 ].fDeltaX = 0;
  311.                         sObjects[ 8 ].fDeltaY = 0;
  312.                     }
  313.                 break;
  314.             case ')':        // freeze/unfreeze this number
  315.                 if ( sObjects[ 9 ].fDeltaX == 0 && sObjects[ 9 ].fDeltaY == 0 )
  316.                     {
  317.                         sObjects[ 9 ].fDeltaX = Random() << 4;
  318.                         sObjects[ 9 ].fDeltaY = Random() << 4;
  319.                     }
  320.                 else
  321.                     {
  322.                         sObjects[ 9 ].fDeltaX = 0;
  323.                         sObjects[ 9 ].fDeltaY = 0;
  324.                     }
  325.                 break;
  326.                 
  327.             default:        // unknown key
  328.                 SysBeep( 0 );
  329.                 break;
  330.         }
  331. }
  332.  
  333.  
  334. //
  335. //    doDemoFrame -
  336. //
  337. //    Move the demo ahead one frame -- move the sprites and draw.
  338. //
  339.  
  340. void doDemoFrame( void )
  341. {
  342.     CGrafPtr oldPort;                                        // the graf port that is in place when we are called
  343.     GDHandle oldDevice;                                // the gdevice that is in place when we are called
  344.     signed long indexCounter;            // a counter to scan all the objects
  345.     signed long rectCounter;            // a counter to scan all the rects
  346.     Rect updateRect;                                            // the rect that needs to be updated
  347.     Point drawPoint;                                            // where to draw the sprite
  348.     
  349.     // if we are in step mode, return
  350.     if ( sStepMode )
  351.         {
  352.             return;
  353.         }
  354.     
  355.     // save the current port and gdevice
  356.     GetGWorld( &oldPort, &oldDevice );
  357.     
  358.     // set the offscreen buffer as current ( and lock the pixel map )
  359.     SetGWorld( gOffscreenBuffer, ( GDHandle )kNil );
  360.     LockPixels( gOffscreenPixels );
  361.     
  362.     // move the objects
  363.     moveObjects();
  364.     
  365.     // if we are showing the update,  restore the entire background
  366.     if( sShowUpdateAreaFlag )
  367.         {
  368.             // restore the background
  369.             restoreBackground( &gOffscreenRect );
  370.         }
  371.     
  372.     // draw the sprites in each of the update rects
  373.     for( rectCounter = 0; rectCounter < getUpdateRectCount(); rectCounter++ )
  374.         {
  375.             // get one of the update rects
  376.             getUpdateRect( rectCounter, &updateRect );
  377.             
  378.             // restore the background
  379.             restoreBackground( &updateRect );
  380.             
  381.             // if the user wants it, draw the update rect
  382.             if ( sShowUpdateAreaFlag )
  383.                 {
  384.                     FrameRect( &updateRect );
  385.                 }
  386.  
  387.             // setup to draw the sprites
  388.             startSpriteDraw( &updateRect, gOffscreenPixels );
  389.                         
  390.             // draw the sprites from back to front
  391.             for( indexCounter = kMaxObjects - 1; indexCounter >= 0; indexCounter-- )
  392.                 {
  393.                     if ( sObjects[ indexCounter ].fIsVisible )
  394.                         {
  395.                             drawPoint.h = ( sObjects[ indexCounter ].fIntXPos ) ;
  396.                             drawPoint.v = ( sObjects[ indexCounter ].fIntYPos );
  397.                             drawSprite( sObjects[ indexCounter ].fSpriteInfo, drawPoint );
  398.                         }
  399.                 }
  400.             
  401.             // shutdown the shape draw
  402.             endSpriteDraw();
  403.         }
  404.  
  405.     if ( sShowUpdateAreaFlag )
  406.         {
  407.             // copy the buffer to the screen
  408.             blitToScreen( &gOffscreenRect );            
  409.         }
  410.     else
  411.         {
  412.             // copy each of the update rects to the screen
  413.             for( rectCounter = 0; rectCounter < getUpdateRectCount(); rectCounter++ )
  414.                 {
  415.                     // get one of the update rects
  416.                     getUpdateRect( rectCounter, &updateRect );
  417.                 
  418.                     // copy the buffer to the screen
  419.                     blitToScreen( &updateRect );            
  420.                 }
  421.         }
  422.     
  423.     // restore the current port and gdevice
  424.     UnlockPixels( gOffscreenPixels );
  425.     SetGWorld( oldPort, oldDevice );
  426. }
  427.  
  428.  
  429. //
  430. //    moveObjects -
  431. //
  432. //    Move all the objects.
  433. //
  434.  
  435. void moveObjects( void )
  436. {
  437.     unsigned short indexCounter;            // a counter to scan all the objects
  438.     Rect objectRect;                                                            // the rectangle that bounds the object
  439.     
  440.     // clear the update area
  441.     clearUpdate();
  442.     
  443.     // concider each object
  444.     for( indexCounter = 0; indexCounter < kMaxObjects; indexCounter++ )
  445.         {
  446.             // if this object has no velocity, we can ignore it
  447.             if ( sObjects[ indexCounter ].fDeltaX == 0 && sObjects[ indexCounter ].fDeltaY == 0 )
  448.                 {
  449.                     continue;
  450.                 }
  451.             
  452.             // if the object was visible last time
  453.             if ( sObjects[ indexCounter ].fWasVisible )
  454.                 {
  455.                     // determine the object's rect
  456.                     objectRect = ( sObjects[ indexCounter ].fSpriteInfo )->fSpriteRect;
  457.                     objectRect.left += sObjects[ indexCounter ].fIntXPos;
  458.                     objectRect.top += sObjects[ indexCounter ].fIntYPos;
  459.                     objectRect.right += sObjects[ indexCounter ].fIntXPos;
  460.                     objectRect.bottom += sObjects[ indexCounter ].fIntYPos;
  461.         
  462.                     // add the old rect to the update (so it will be erased)
  463.                     addRectToUpdate( &objectRect );
  464.                 }
  465.  
  466.             // apply the velocity
  467.             sObjects[ indexCounter ].fXPos += sObjects[ indexCounter ].fDeltaX;
  468.             sObjects[ indexCounter ].fYPos += sObjects[ indexCounter ].fDeltaY;
  469.             
  470.             // if the object has hit an edge, bounce it
  471.             if( sObjects[ indexCounter ].fXPos < ( gOffscreenRect.left << 16 ) )
  472.                 {
  473.                     sObjects[ indexCounter ].fDeltaX *= -1;
  474.                     sObjects[ indexCounter ].fXPos += ( gOffscreenRect.left << 16 ) - sObjects[ indexCounter ].fXPos;
  475.                 }
  476.             if( sObjects[ indexCounter ].fYPos < ( gOffscreenRect.top << 16 ) )
  477.                 {
  478.                     sObjects[ indexCounter ].fDeltaY *= -1;
  479.                     sObjects[ indexCounter ].fYPos += ( gOffscreenRect.top << 16 ) - sObjects[ indexCounter ].fYPos;
  480.                 }
  481.             if( sObjects[ indexCounter ].fXPos + 
  482.                     ( ( ( sObjects[ indexCounter ].fSpriteInfo )->fSpriteRect.right -
  483.                     ( sObjects[ indexCounter ].fSpriteInfo )->fSpriteRect.left ) << 16 ) >
  484.                     ( gOffscreenRect.right << 16 ) )
  485.                 {
  486.                     sObjects[ indexCounter ].fDeltaX *= -1;
  487.                     sObjects[ indexCounter ].fXPos += ( ( gOffscreenRect.right - 
  488.                             ( ( sObjects[ indexCounter ].fSpriteInfo )->fSpriteRect.right -
  489.                             ( sObjects[ indexCounter ].fSpriteInfo )->fSpriteRect.left ) ) << 16 ) - 
  490.                             sObjects[ indexCounter ].fXPos;
  491.                 }
  492.             if( sObjects[ indexCounter ].fYPos + 
  493.                     ( ( ( sObjects[ indexCounter ].fSpriteInfo )->fSpriteRect.bottom -
  494.                     ( sObjects[ indexCounter ].fSpriteInfo )->fSpriteRect.top ) << 16 ) >
  495.                     ( gOffscreenRect.bottom << 16 ) )
  496.                 {
  497.                     sObjects[ indexCounter ].fDeltaY *= -1;
  498.                     sObjects[ indexCounter ].fYPos += ( ( gOffscreenRect.bottom - 
  499.                             ( ( sObjects[ indexCounter ].fSpriteInfo )->fSpriteRect.bottom -
  500.                             ( sObjects[ indexCounter ].fSpriteInfo )->fSpriteRect.top ) ) << 16 ) - 
  501.                             sObjects[ indexCounter ].fYPos;
  502.                 }
  503.             
  504.             // if the object is visible
  505.             if( sObjects[ indexCounter ].fIsVisible )
  506.                 {
  507.                     // evaluate the fixed numbers to new integer positions
  508.                     sObjects[ indexCounter ].fIntXPos = ( sObjects[ indexCounter ].fXPos ) >> 16;
  509.                     sObjects[ indexCounter ].fIntYPos = ( sObjects[ indexCounter ].fYPos ) >> 16;
  510.                     
  511.                     // determine the object's rect
  512.                     objectRect = ( sObjects[ indexCounter ].fSpriteInfo )->fSpriteRect;
  513.                     objectRect.left += sObjects[ indexCounter ].fIntXPos;
  514.                     objectRect.top += sObjects[ indexCounter ].fIntYPos;
  515.                     objectRect.right += sObjects[ indexCounter ].fIntXPos;
  516.                     objectRect.bottom += sObjects[ indexCounter ].fIntYPos;
  517.         
  518.                     // add the new rect to the update (so it will be drawn)
  519.                     addRectToUpdate( &objectRect );
  520.                 }
  521.             
  522.             // record the this frame's visibility
  523.             sObjects[ indexCounter ].fWasVisible = sObjects[ indexCounter ].fIsVisible;
  524.         }
  525. }
  526.  
  527.  
  528. //
  529. //    blitToScreen -
  530. //
  531. //    This routine updates the screen from the offscreen buffer.
  532. //
  533.  
  534. void blitToScreen( Rect *inCopyRect )
  535. {
  536.     CGrafPtr oldPort;                    // the graf port that is in place when we are called
  537.     GDHandle oldDevice;            // the gdevice that is in place when we are called
  538.  
  539.     // save the current port and gdevice
  540.     GetGWorld( &oldPort, &oldDevice );
  541.  
  542.     // set the drawing environment to the screen
  543.     SetGWorld( ( CWindowPtr )gMainWindow, GetMainDevice() );
  544.  
  545.     // make sure that the fore and back colors are correct to prevent colorize mode
  546.     ForeColor( blackColor );
  547.     BackColor( whiteColor );
  548.     
  549.     // Copy the screen's color table seed into the source pixmap.
  550.     // This will minimize CopyBits' setup time.
  551.     ( *( ( *gOffscreenPixels )->pmTable ) )->ctSeed = ( *( ( *( ( *( GetGDevice() ) )->gdPMap ) )->pmTable ) )->ctSeed;
  552.  
  553.     // copy the buffer to the screen
  554.     CopyBits( ( BitMap * )( *gOffscreenPixels ), &( gMainWindow->portBits ), 
  555.             inCopyRect, inCopyRect, srcCopy, ( RgnHandle )kNil );
  556.  
  557.     // restore the current port and gdevice
  558.     SetGWorld( oldPort, oldDevice );
  559. }
  560.